[CLI-321] Add and use a Converter interface and implementations without using BeanUtils#216
Conversation
Adds BeanUtils dependency to POM and implements TypeHandler using the BeanUtils classes. Handles all methods that were in the original TypeHandler as well as other default classes provided by BeanUtils. Test updated to show proper parsing of types that previously were not implemented. Includes new cli.converters package that may be better handled by BeanUtils as it moves forward to support FunctionalInterfaces.
Added getParsedOptionValues methods with default values. Fixed som javadoc,
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #216 +/- ##
============================================
- Coverage 93.20% 92.01% -1.19%
- Complexity 570 582 +12
============================================
Files 21 22 +1
Lines 1206 1253 +47
Branches 216 210 -6
============================================
+ Hits 1124 1153 +29
- Misses 46 63 +17
- Partials 36 37 +1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
Hi @Claudenw |
|
@garydgregory I think this might be ready for prime time now. |
garydgregory
left a comment
There was a problem hiding this comment.
Hello @Claudenw
Thank you for this PR.
I think the best path is to work toward getting this PR in instead of the BeanUtils version.
Please see my comments, which are few, per my issue formatting.
| * @since 1.7 | ||
| */ | ||
| @FunctionalInterface | ||
| public interface Verifier { |
There was a problem hiding this comment.
Probably more flexible if this extends Predicate<String>.
There was a problem hiding this comment.
I think we can get rid of Verifier and just use Predicate
There was a problem hiding this comment.
Note that the advantage of a custom interface here is that we are cementing the design to only test String input, as opposed CharSequence for example (or anything else). So we should make it clear at least in our mind what the intent is here or if there should be such an input-type restriction.
| /** | ||
| * The definition of the functional interface to call when verifying a string | ||
| * for input Like {@code Predicate<String>} but can throw a RuntimeException. | ||
| * @since 1.7 |
There was a problem hiding this comment.
The next feature version will be 1.7.0 (major.minor.maintenance).
| * | ||
| * @param str the File location | ||
| * @return The file represented by {@code str}. | ||
| * @param str the File location |
There was a problem hiding this comment.
Please revert the formatting changes. It makes the PR noisier, larger and takes longer to review.
| * @since 1.7 | ||
| */ | ||
| @FunctionalInterface | ||
| public interface Converter<T> { |
There was a problem hiding this comment.
Probably should extend Function<String, T>.
There was a problem hiding this comment.
It can't since Converter throws a ParseException and Function<String,T> does not allow that.
There was a problem hiding this comment.
Right, we don't want to depend on Commons Lang's FailableException here.
Next: Throwing Exception is an anti-pattern and should be avoided at all costs IMO. Either:
- reconsider and throw runtime exception OR
- throw a more specific checked exception OR
- parameterize the exception type
| Integer.class); | ||
| checkOption(Option.builder().option("a").desc("desc").type(Integer.class).build(), "a", "desc", null, Option.UNINITIALIZED, null, false, false, | ||
| defaultSeparator, Integer.class); | ||
| checkOption(Option.builder("a").desc("desc").build(), "a", "desc", null, Option.UNINITIALIZED, null, false, |
There was a problem hiding this comment.
What's changed here? If it's just formatting, please revert, I don't want to take the time to tease apart formatting from feature changes.
|
How about adding support for |
|
@garydgregory Sorry about the formatting. What would you want to see for Path? I think that Verifier class should become a static class that simply contains static common Predicate instances. Where I don't think there needs to be a difference. At one time I had the Verifier throwing the ParseException, but that has been fixed, so there is no valid distinction. What are your thoughts? I also want to move everything in o.a.c.cli.converters into o.a.c.cli |
Path: feature parity with File. |
Which can be done later. |
|
Hi @Claudenw Hm, I think this PR is overly complicated. Specifically, I do not think there should be a split between verifying and converting, there should not be a whole For a simple example, it is legal for an Integer to have a leading It's worse if I want to have a Double parameter since the RE for that is large and complex as documented in All of that to say, that you don't have to worry about any of this if you let a converter do its usual parsing work without trying to short-circuit the process with an additional parsing step. IOW, don't parse input twice, we don't need verifiers. Nits:
|
Verifier interface became static class to hold common Predicate<String> instances for verification. Moved EnumVerifier into Verifier as a static method. All references to Verifier as a data type chagned to Predicate<String>.
|
@garydgregory I have added the Path to the standard converters handled by the TypeHandler and added documentation for the entire span of this change. I also added a set of getOptionValue() methods that accept a Supplier to create the defaults. |
garydgregory
left a comment
There was a problem hiding this comment.
I'll keep repeating it ;-) We don't need a "verifier". If later, this proves a requirement, we can discuss adding that concept. I still think that "verifying" is doomed to fail with both false positives and false negatives, on top of being redundant, since "verification" is done implicitly when parsing, for example by Double#parseDouble().
|
In working on Cassandra Stress test tool where we are trying to move to commons-cli there are cases where we want to quickly identify incorrect textual input without having to go through expensive conversion processes. This also gives an easy way to verify things like enum values and provide better error messages than if you just call I did remove the Verifier management code so we are not setting them by default and the TypeHandler does not set them either. Much like setting the |
|
@garydgregory I removed the verifier code and documentation. |
garydgregory
left a comment
There was a problem hiding this comment.
@Claudenw
Thank you for your updates. Please see my comments.
| * Converts any exception except {@code UnsupportedOperationException} to a {@code ParseException}. | ||
| * if {@code e} is an instance of {@code ParseException} it is returned, otherwise a {@code ParseException} is | ||
| * created that wraps it. | ||
| * <p> |
There was a problem hiding this comment.
Hello @Claudenw
- Close HTML tags
- Add
@sincetags to newpublicandprotectedelements.
| if (e instanceof UnsupportedOperationException) { | ||
| throw (UnsupportedOperationException) e; | ||
| } | ||
|
|
|
|
||
| /** | ||
| * Registers custom {@code Converter}s with the {@code TypeHandler}. | ||
| */ |
There was a problem hiding this comment.
Add @since tags to new public and protected elements.
| * | ||
| * @param ch the specified character | ||
| * @return The class that {@code ch} represents | ||
| * @deprecated use {@link #getValueType(char)} |
There was a problem hiding this comment.
Add @Deprecated annotation to the method.
| * @param ch the specified character | ||
| * @return The class that {@code ch} represents | ||
| */ | ||
| public static Class<?> getValueType(final char ch) { |
There was a problem hiding this comment.
Add @since tags to new public and protected elements.
| } | ||
|
|
||
| /** | ||
| * Registers a Converter for a Class. If @code converter} is null registration is cleared for {@code clazz}, and |
There was a problem hiding this comment.
Only one space after the ..
| System.out.println("Unexpected exception:" + exp.getMessage()); | ||
| }</source> | ||
| </section> | ||
| <section name="Converting (Parsing) Option Values"> |
|
@garydgregory requested changes made. Also found old 'verifier' documentation still in place and removed it. |
| */ | ||
| public static void resetConverters() { | ||
| converterMap.clear(); | ||
| converterMap.put(Object.class, Converter.OBJECT); |
There was a problem hiding this comment.
Hi @Claudenw,
We have a mix of using constants and expressions to define this map. I think it would be better to do it one way or another: Use all constants or use all expressions.
The simplest would be to inline the constants from Converter and decrease the new public footprint. Alternatively, to keep constants we could make them package-private in a new or existing class.
Any thoughts on this?
https://issues.apache.org/jira/browse/CLI-321
Similar to #215 but without using BeanUtils.
Implementation introduces Converter and Verifier interfaces.
Converter does as it says and converts from Str to object.
Verifier verifies that string values are proper before doing the conversion. Checks are made when the string is added to the values in the Option object.
TypeHandler is reworked to use and manage the mapping of the Converter and Verifier instances to Classes.
Methods are provided to register Converter and Verifier for a class.
Tests added for Converters and Verifiers as well as TypeHandler changes.
Changes to earlier tests:
There was a TypeHandler test that expected the value "3,5" to throw and exception when it was retrieved, that now throws an exception when the value is initially added to the Option during command line parsing. Test has been updated to account for this.
Some javadocs need to be completed.